最近在读阮一峰的《ECMAScript 6 入门》,之前接触es6只知道一些常见的特性,原理并不深入,通过这本书收获很多。之前网上看到过一个题目,问到使用const来定义对象会发生什么,能不能改变值?最初看到这个题目的时候,第一反应就是const是用来定常量的,定义了就不能改变,非常的想当然,但是并没有深入了解原理,现在重新阅读了这本书之后,发现以前的理解有许多偏差,因此也收获了很多,以下是一些读书笔记,将陆续更新
1、babel-polyfill
Bable默认只转换新的javascript语法,而不转换新的API,比如iterator、gennerator、set、map、proxy、reflect、symbol、promise等全局对象。
举例来说,ES6在Array对象上新增了Array.from方法,babel就不会转换这个方法,需要通过bable-polyfill来进行转换
2、ESLint
用于静态检查代码的语法和风格
3、let
- 用来声明变量,用法类似于var, 但是声明的变量,旨在let命令的代码块内有效。
1 | var a = []; |
- 不存在变量提升
在var情况下,变量在声明之前使用,虽然值为undefined,但是不会报错,但是由于let不存在变量提升,因此会报错
1 | // var 的情况 |
- 暂时性死区
只要块级作用域内存在let命令,他所声明的变量就绑定在这个区域,不受外部影响
1 | var tmp = 123; |
- 不允许重复声明
块级作用域
1
2
3
4{{{{
{let insane = 'Hello World'}
console.log(insane); // 报错
}}}};块级作用域与函数声明
在ES5中,函数只能在顶层作用域中声明,不能再块级作用域中声明,
const
- const声明一个只读的常量,一旦声明,常量的值就不能改变。
- const一旦声明,就必须立即初始化,不能六道以后复制,否则会报错。
- 本质
const实际上保证的并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。
对于简单类型的数据(数值、字符串、布尔值),值就保存在指向的内存地址,因此等同于常量。
但对于复合类型的数据(主要是对象和数组),指向的只是一个指向实际数据的指针,const只能保证这个指针固定不懂,至于指向的数据结构是不是可变,就不能控制了
1 | const foo = {}; |
若真的想将对象冻结,应该使用Object.freeze()
声明变量的六种方法
var function let const import class
顶层对象的属性
顶层对象,在浏览器环境指的是window对象,在node指的是global对象
ES5中,顶层对象和全局变量是等价的,而在ES6中,let、const、class声明的全局变量,不属于顶层对象的属性。
变量的结构赋值
从数组和对象中提取值,对变量进行赋值,被称之为解构。只要等号两边的模式相同,左边的变量就会被赋予对应的值。
1 | let [foo, [[bar], baz]] = [1, [[2], 3]]; |
只要某种数据结构具有Iterator接口,都可以采用数组形式的解构赋值。
字符串的扩展
ES6为字符串添加了遍历器接口,是的字符可以被for ..of 循环遍历
1 | for(let codePoint of 'foo'){ |
includes(), startsWith(), endsWith()
传统上,只有indexOf方法可以用来判断一个字符串是否包含另一个字符串。es6提供了以下三种新方法
- includes() :返回布尔值,表示是否找到了参数字符串
- startsWith():返回布尔值,表示参数字符串是否在原字符串的头部
- endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部
这三种方法都支持第二个参数,表示开始搜索的位置
repeat()
repeat()返回一个新字符串,表示将源字符串重复n次,只针对字符串做操作!
1 | 'x'.repeat(3) //xxx |
模板字符串
1 | let name ='cindy' |
若使用模板字符串表示多行字符串,所有的空格和缩进都会被保留在输出之中
字符串模板中还能调用函数1
2
3
4
5function fn(){
return "Hello world"
}
`foo ${fn()} bar` //"foo Hello world bar"
大括号内可以放任意表达式、函数、变量
正则的扩展
数值的扩展
函数的扩展
函数参数允许指定默认值
1 | function log(x,y = 'world'){ |
函数的length属性
返回没有置顶默认值的参数个数1
2(function (a){}).length //1
(function (a=1){}),length //0
箭头函数
可以使用一个圆括号代表参数部分1
2
3
4
5
6
7
8
9
10
11var f = () =>5
//相当于
var f = function (){
return 5
}
var sum = (num1,num2)=>num1+num2;
//等同于
var sum = function (num1,num2){
return num1+num2;
}
箭头函数的使用注意点
- 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象!
- 不可以当作构造函数,也就是说,不能使用new,否则会抛出一个错误。
- 使用rest代替arguments对象
- 箭头函数不可以作为generator函数
==this对象的指向是可变的,但是在箭头函数中,他是固定的==
箭头函数根本没有自己的this,导致内部this就是外层代码块的this,正是因为没有this, 所以不能用作构造函数。
1 | // ES6 |
请问下面的代码之中有几个this?
1 | function foo() { |
双冒号运算符
由于箭头函数并不适用于所有场合,因此“函数绑定”运算符,用来取代call、bind、apply调用
函数绑定运算符是并排的两个冒号(::),双冒号左边是一个对象,右边是一个函数。该运算符会自动将左边的对象,作为上下文环境(即this对象),绑定到右边的函数上面。
1 | foo::bar; |
代替函数的apply方法
由于扩展运算符可以展开数组,因此不再需要apply方法,下面是利用扩展运算符求一个数组最大元素的例子1
2
3
4
5
6
7
8//es5
Math.max.apply(null,[1,23,4,5,3])
//es6
Math.max(...[1,23,4,5,3])
//等同于
Math.max(1,23,4,5,3)
扩展运算符的应用
复制数组
es5中复制数组的时候只是复制了底层数据结构的指针,更改a2会影响a1,但使用了扩展运算符会避免这个问题1
2
3
4
5const a1=[1,2]
const a2=[...a1]
a2[0]=0
a2 //[0,2]
a1 //[1,2]合并数组
1
[...arr1,...arr2,...arr3]
Array.from()
Array.from 方法用于将两类对象转为真正的数组。1
2
3
4
5
6
7
8let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
Array.of()
Array.of()用于将一组数值,转换为数组
对象的扩展
属性的便利
- for…in 循环遍历对象自身的和继承的可枚举属性
- Object.keys(obj) 返回一个数组,包括对象自身(不含继承)所有枚举属性的键名
- Object.getOwnPropertyNames(obj) 返回一个数组,包含对象自身的所有属性的键名
- Object.getOwnPropertySymbols(obj)返回一个数组,包含对象自身的所有 Symbol 属性的键名。
- Reflect.ownKeys(obj) 返回一个数组,包含对象自身的所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。
super 关键字
指向当前对象的原型对象
对象新增的方法
Object.is()
用来比较两个值是否严格相等。
1 | Object.is('foo', 'foo') |
Object.assign()
Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。Object.assign方法的第一个参数是目标对象,后面的参数都是源对象。
1 | const target = { a: 1, b: 1 }; |
- Object.assign 实行的是浅拷贝
Set
类似于数组,但是成员的值都是唯一的,因此可以用来去重
1 | [...new Set(array)] |